Skip to content

Restore crypto.Signer for classical foreign JWS (fix #34) - #36

Merged
yaronf merged 2 commits into
mainfrom
fix/crypto-signer-jws
Oct 2, 2026
Merged

yaronf merged 2 commits into
mainfrom
fix/crypto-signer-jws

Conversation

@yaronf

@yaronf yaronf commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Summary

@ilya-korotya — please review and run this branch against your HSM/KMS-backed signer (the case that broke in v0.6.x). CI can’t exercise real remote keys; your run is the validation that matters.

Made with Cursor

Accept opaque HSM/KMS Signers for RSA/ECDSA/Ed25519 on NewJWSSigner and
NewJWSVerifierWithAlg again; document v0.6.2 and credit the reporter.

Co-authored-by: Cursor <cursoragent@cursor.com>
@yaronf

yaronf commented Sep 30, 2026

Copy link
Copy Markdown
Owner Author

@ilya-korotya thanks again for #34 / #35. Formal review request isn’t available (not a repo collaborator), but please check out this PR and run it against your HSM/KMS-backed crypto.Signer (sign + verify if you use the same wrapper for both). That real-world path is what we need beyond CI.

@ilya-korotya

Copy link
Copy Markdown

Thanks for the fix, the classical algorithms work for me now!

My main goal though was ML-DSA with the private key stored in AWS KMS, and that still fails, because ML-DSA only accepts a raw *mldsa.PrivateKey. I get that jwx built-in ML-DSA signer needs a raw key. But this can be worked around with jws.RegisterSigner: I register my own ML-DSA signer that just calls crypto.Signer.Sign, and with that everything works end to end.

This is what I register:

func init() {
      for _, alg := range []jwa.SignatureAlgorithm{jwa.MLDSA44(), jwa.MLDSA65(), jwa.MLDSA87()} {
              if err := jws.RegisterSigner(alg, mldsaCryptoSigner{}); err != nil {
                      panic(fmt.Sprintf("register ML-DSA signer for %s: %v", alg, err))
              }
      }
}

// mldsaCryptoSigner signs through crypto.Signer (e.g. a KMS-backed key)
// instead of requiring a raw *mldsa.PrivateKey.
type mldsaCryptoSigner struct{}

func (mldsaCryptoSigner) Sign(key any, payload []byte) ([]byte, error) {
      signer, ok := key.(crypto.Signer)
      if !ok {
              return nil, fmt.Errorf("unsupported key type %T, want crypto.Signer", key)
      }
      if _, ok := signer.Public().(*mldsa.PublicKey); !ok {
              return nil, fmt.Errorf("key's Public() is %T, want *mldsa.PublicKey", signer.Public())
      }
      // ML-DSA signs the message itself, no pre-hashing.
      return signer.Sign(nil, payload, &mldsa.Options{})
}

With this in place, the only thing that still blocks me is validateMLDSAKey, which rejects the crypto.Signer before jwx is ever called. On a build where mldsaKeyOf accepts a crypto.Signer (as in my draft #35: it takes the parameter set from Public() and keeps the parameter-set check), signing with the KMS key works fine.

So could the ML-DSA part of #35 be kept in #36? Or, alternatively, would you consider adding a native ML-DSA signer that takes a crypto.Signer directly and doesn't go through jwx at all?

Construction validates Public() and parameter set; Sign still needs stock
*mldsa.PrivateKey or a custom jws.RegisterSigner (document + test).

Co-authored-by: Cursor <cursoragent@cursor.com>
@yaronf

yaronf commented Oct 1, 2026

Copy link
Copy Markdown
Owner Author

@ilya-korotya thanks for the classical confirmation and the ML-DSA / RegisterSigner details.

We extended this PR so opaque crypto.Signer is accepted for ML-DSA at construction too (Public() → *mldsa.PublicKey + parameter-set check), matching your draft #35 idea. Stock jwx Sign still wants *mldsa.PrivateKey; with your jws.RegisterSigner workaround, httpsign should no longer block you in validateMLDSAKey.

Could you please re-run against your AWS KMS ML-DSA signer on this branch and confirm end-to-end? (We deliberately did not add a native ML-DSA signer — ML-DSA stays foreign-JWS only.)

@ilya-korotya

ilya-korotya commented Oct 2, 2026 •

Copy link
Copy Markdown

Hi @yaronf, these changes worked for me. Thank you very much for your help.

@yaronf
yaronf merged commit 6c5054f into main Oct 2, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants